﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RoketPack.IM
{
    public partial class ChatWindow : Form
    {
        private MessageQueue m_MessageQueue = null;
        private Color m_UserColor = Color.FromArgb(32, 74, 135);
        private Color m_ContactColor = Color.FromArgb(204, 0, 0);
        private Contact m_Contact = null;
        private Contact m_User = null;

        /// <summary>
        /// Creates a new ChatWindow with the specified message queue (for
        /// sending and recieving messages though) and the specified contact.
        /// </summary>
        /// <param name="queue"></param>
        /// <param name="contact"></param>
        internal ChatWindow(MessageQueue queue, Contact contact, Contact user)
        {
            InitializeComponent();

            this.m_MessageQueue = queue;
            this.m_Contact = contact;
            this.m_User = user;
            this.c_UserStatus.Username = contact.Representation;
            this.c_UserStatus.Status = "";
        }

        /// <summary>
        /// Appends a new sent message to the message window with
        /// the specified text.
        /// </summary>
        /// <param name="contact">The contact the message was sent to.</param>
        /// <param name="msg">The message data.</param>
        internal void AppendSentMessage(Contact contact, string msg)
        {
            this.Text = contact.Representation;
            this.AppendMessage(this.m_UserColor, this.m_User.Representation + ":", msg);
        }

        /// <summary>
        /// Appends a new recieved message to the message window
        /// with the specified contact's name and message text.
        /// </summary>
        /// <param name="contact">The contact the message was recieved from.</param>
        /// <param name="msg">The message data.</param>
        internal void AppendRecievedMessage(Contact contact, string msg)
        {
            this.Text = contact.Representation;
            this.AppendMessage(this.m_ContactColor, contact.Representation + ":", msg);

            if (!this.Focused)
                Aero.Attention(this);
        }

        /// <summary>
        /// Updates the status and state shown on the UserStatus control to
        /// match that currently stored in the specified Contact instance.
        /// </summary>
        /// <param name="contact">The contact instance to retrieve the state and status information from.</param>
        internal void UpdateStatus(Contact contact)
        {
            this.c_UserStatus.Username = contact.Representation;
            this.c_UserStatus.Status = contact.Status;
            this.c_UserStatus.State = contact.State;
            this.Icon = Aero.MakeIcon(contact.StateImage, 16, true);
            this.Text = contact.Representation;
        }

        /// <summary>
        /// This event is raised when the user presses a key within the input area.
        /// It is used to capture Enter key presses and send the message to the
        /// contact.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The keyboard event information.</param>
        private void c_InputArea_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter)
                return;

            this.m_MessageQueue.Send(this.m_Contact, this.c_InputArea.Text);
            this.c_InputArea.Text = "";
            e.Handled = true;
        }

        /// <summary>
        /// A generic function used for appending new messages to the message
        /// window, with the specified color, username and message text.
        /// </summary>
        /// <param name="c">The color to show the username in.</param>
        /// <param name="user">The username to show.</param>
        /// <param name="msg">The message text.</param>
        private void AppendMessage(Color c, string user, string msg)
        {
            bool bottom = this.c_MessageWindow.IsAtBottom;
            this.c_MessageWindow.SelectionLength = 0;
            this.c_MessageWindow.SelectionStart = this.c_MessageWindow.TextLength;
            string rtf = "{\\rtf1\\ansi{{\\colortbl ;" + ChatWindow.RTFColor(c) +
                         "}\\cf1 {\\fs12 (" + this.GetTimeText() + ")} {\\b " + ChatWindow.EscapeRTF(user) + "}} " +
                         ChatWindow.EscapeRTF(msg + "\r\n") + "}";
            this.c_MessageWindow.SelectedRtf = rtf;

            if (bottom)
                this.c_MessageWindow.ScrollToBottom();
        }

        /// <summary>
        /// Gets the current time as a text string to be shown within the
        /// message window.
        /// </summary>
        /// <returns>The current time as a text string.</returns>
        private string GetTimeText()
        {
            DateTime time = DateTime.Now;
            int h = time.Hour;
            string ampm = "AM";
            if (h > 12)
            {
                h -= 12;
                ampm = "PM";
            }
            string m = time.Minute.ToString();
            string s = time.Second.ToString();
            if (m.Length == 1) m = "0" + m;
            if (s.Length == 1) s = "0" + s;

            return h + ":" + m + ":" + s + " " + ampm;
        }

        /// <summary>
        /// Retrieves the RTF representation of a color.
        /// </summary>
        /// <param name="c">The color.</param>
        /// <returns>The RTF representation of the specified color.</returns>
        private static string RTFColor(Color c)
        {
            return String.Format("\\red{0}\\green{1}\\blue{2};", c.R, c.G, c.B);
        }

        /// <summary>
        /// Escapes the text to be sent or recieved with RTF formatting standards.
        /// </summary>
        /// <param name="plain">The plain text.</param>
        /// <returns>The RTF formatted text.</returns>
        private static string EscapeRTF(string plain)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder(plain.Length);

            char state = 'c';

            foreach (char c in plain)
            {
                if (state == 'r')
                {
                    if (c == '\n')
                        state = 'n';
                    else
                    {
                        state = 'c';
                    }
                }
                else if (state == 'n')
                {
                    state = 'c';
                }

                if (state == 'c')
                {
                    if (c > 127)
                    {
                        sb.Append(@"\u");
                        sb.Append((int)c);
                        sb.Append('?');
                    }
                    else
                    {
                        switch (c)
                        {
                            case '{':
                            case '}':
                            case '\\':
                                sb.Append('\\');
                                sb.Append(c);
                                break;
                            case '\r':
                                sb.Append("\\\r\n");
                                state = 'r';
                                break;
                            case '\n':
                                sb.Append("\\\r\n");
                                state = 'n';
                                break;
                            default:
                                sb.Append(c);
                                break;
                        }
                    }
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// This event is raised when the Clear Conversation context menu item
        /// is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_ClearConversationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.c_MessageWindow.ClearAndScroll();
        }

        /// <summary>
        /// This event is raised when the End Conversation context menu item
        /// is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_EndConversationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
